package com.corewillsoft.fireflies.storage;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import android.content.ContentValues;
import android.database.Cursor;

import com.corewillsoft.fireflies.gameobjects.GameObject;
import com.corewillsoft.fireflies.storage.entries.MapLevelEntries;
import com.corewillsoft.fireflies.storage.entries.SavedGameLevelEntries;
import com.corewillsoft.fireflies.storage.entries.StartGameLevelEntries;
import com.corewillsoft.fireflies.storage.objects.BaseGameLevel;
import com.corewillsoft.fireflies.storage.objects.MapLevel;
import com.corewillsoft.fireflies.storage.objects.StartGameLevel;
import com.corewillsoft.fireflies.util.ScreenUtils;
import com.corewillsoft.fireflies.util.TypesConverter;

public class DbManager {

	private final DataAdapter dataAdapter;

	public DbManager() {
		this.dataAdapter = DataAdapter.getInstance();
	}

	public void addMapLevel(MapLevel mapLevel) {
		MapLevel rescaledLevel = mapLevel;//rescaleMapLevel(mapLevel);
		// check if level with this number already exists in DB
		int thisLevelCountInDB = dataAdapter.count(MapLevelEntries.TABLE_NAME,
				MapLevelEntries.LEVEL + "=?",
				new String[] { String.valueOf(rescaledLevel.getLevel()) });

		// insert level data only if it was not previously saved in DB
		if (thisLevelCountInDB == 0) {
			dataAdapter.insert(MapLevelEntries.TABLE_NAME, null,
					rescaledLevel.toContentValues());
		}
	}
	
	private MapLevel rescaleMapLevel(MapLevel oldLevel) {
		float scale = ScreenUtils.getInstance().getScaleRatio();
		int x = (int) (oldLevel.getCoordinate().getX() / scale);
		int y = (int) (oldLevel.getCoordinate().getY() / scale);
		int width = (int) (oldLevel.getWidth() / scale);
		int height = (int) (oldLevel.getHeight() / scale);
		
		MapLevel newLevel = new MapLevel(oldLevel.getLevel(), x, y, width, height, oldLevel.getImageRef());
		
		return newLevel;
	}

	public List<MapLevel> getMapLevels() {
		Cursor cursor = dataAdapter.query(MapLevelEntries.TABLE_NAME,
				MapLevelEntries.ALL_COLUMN_NAMES, null, null, null, null, null);

		if (cursor == null) {
			return Collections.emptyList();
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return Collections.emptyList();
		}

		List<MapLevel> result = new ArrayList<MapLevel>();
		do {
			result.add(ObjectsConverter.convertMapLevelFromCursor(cursor));
		} while (cursor.moveToNext());
		cursor.close();

		return result;
	}

	public MapLevel getMapLevel(int levelNumber) {
		String selectionByLevel = MapLevelEntries.LEVEL + "=?";
		String[] selectionByLevelArgs = new String[] { String
				.valueOf(levelNumber) };
		Cursor cursor = dataAdapter.query(MapLevelEntries.TABLE_NAME,
				MapLevelEntries.ALL_COLUMN_NAMES, selectionByLevel,
				selectionByLevelArgs, null, null, null);

		if (cursor == null) {
			return null;
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return null;
		}

		MapLevel result = ObjectsConverter.convertMapLevelFromCursor(cursor);
		cursor.close();

		return result;

	}

	public void addGameLevel(BaseGameLevel gameLevel) {
		BaseGameLevel rescaledLevel = gameLevel;//rescaleGameLevel(gameLevel);
		// check if level with this number already exists in DB
		int thisLevelCountInDB = dataAdapter.count(
				StartGameLevelEntries.TABLE_NAME, StartGameLevelEntries.LEVEL
						+ "=?",
				new String[] { String.valueOf(rescaledLevel.getLevel()) });

		// insert level data only if it was not previously saved in DB
		if (thisLevelCountInDB == 0) {
			dataAdapter.insert(StartGameLevelEntries.TABLE_NAME, null,
					rescaledLevel.toContentValues());
		}
	}

	private BaseGameLevel rescaleGameLevel(BaseGameLevel oldGameLevel) {
		BaseGameLevel newLevel = new StartGameLevel();
		
		newLevel.setFireflyAlive(oldGameLevel.getFireflyAlive());
		newLevel.setFireflyEnergy(oldGameLevel.getFireflyEnergy());
		newLevel.setFireflyIds(oldGameLevel.getFireflyIds());
		newLevel.setFireflySaved(oldGameLevel.getFireflySaved());
		newLevel.setFireflyX(rescaleListOfCoordinates(oldGameLevel.getFireflyX()));
		newLevel.setFireflyY(rescaleListOfCoordinates(oldGameLevel.getFireflyY()));
		newLevel.setLevel(oldGameLevel.getLevel());
		newLevel.setObstacleIds(oldGameLevel.getObstacleIds());
		// TODO rescale parameters
		newLevel.setObstacleX(rescaleListOfCoordinates(oldGameLevel.getObstacleX()));
		newLevel.setObstacleY(rescaleListOfCoordinates(oldGameLevel.getObstacleY()));
		newLevel.setPortalIds(oldGameLevel.getPortalIds());
		newLevel.setPortalX(rescaleListOfCoordinates(oldGameLevel.getPortalX()));
		newLevel.setPortalY(rescaleListOfCoordinates(oldGameLevel.getPortalY()));
		
		return newLevel;
	}
	
	
	
	private List<Integer> rescaleListOfCoordinates(List<Integer> oldList) {
		float scale = ScreenUtils.getInstance().getScaleRatio();
		List<Integer> result = new ArrayList<Integer>();
		for (Integer old : oldList) {
			int rescaled = (int) (old / scale);
			result.add(rescaled);
		}
		
		return result;
	} 
	
	public BaseGameLevel getGameLevel(int level) {
		return getGameLevel(level, true);
	}

	/**
	 * @param level
	 *            number of the level
	 * @param restore
	 *            indicate what data will be loaded. Saved or Start.
	 * @return
	 */

	public BaseGameLevel getGameLevel(float level, boolean newGame) {
		if (newGame) {
			return getStartLevel(level);
		} else {
			return getIncompletedLevel(level);
		}
	}

	private BaseGameLevel getStartLevel(float level) {
		String selection = StartGameLevelEntries.LEVEL + "=?";
		String[] selectionArgs = new String[] { String.valueOf((int) level) };

		Cursor cursor = dataAdapter.query(StartGameLevelEntries.TABLE_NAME,
				StartGameLevelEntries.ALL_COLUMN_NAMES, selection,
				selectionArgs, null, null, null);

		if (cursor == null) {
			return null;
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return null;
		}

		BaseGameLevel result = ObjectsConverter.convertToStartGameLevel(cursor);
		cursor.close();

		return result;
	}

	private BaseGameLevel getIncompletedLevel(float level) {

		String selection = SavedGameLevelEntries.LEVEL + "=?";
		String[] selectionArgs = new String[] { String.valueOf((int) level) };

		Cursor cursor = dataAdapter.query(SavedGameLevelEntries.TABLE_NAME, null,
				selection, selectionArgs, null, null, null);

		if (cursor == null) {
			return null;
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return null;
		}

		BaseGameLevel result = ObjectsConverter.convertToSaveGameLevel(cursor);
		cursor.close();

		return result;
	}

	/**
	 * @param level
	 *            * * - level number
	 * @param gameObjects
	 *            * - collection of the gameobjects
	 */
	public void saveIncompletedGameLevel(int level, Set<GameObject> gameObjects) {
		BaseGameLevel gameLevel = ObjectsConverter.getGameLevel(level, gameObjects);
		saveLevel(gameLevel.toContentValues(), level, false);
	}

	/**
	 * Saves finished game level and game obects state
	 * @param level
	 * @param gameObjects
	 */
	public void saveCompletedGameLevel(int level, Set<GameObject> gameObjects) {
		BaseGameLevel gameLevel = ObjectsConverter.getGameLevel(level, gameObjects);
		saveLevel(gameLevel.toContentValues(), level, true);
	}

	private void saveLevel(ContentValues contentValues, int level, boolean finished) {
		contentValues.put(SavedGameLevelEntries.COMPLETED, TypesConverter.bool2int(finished));
		contentValues.put(SavedGameLevelEntries._ID, toUniqueSaveId(level, finished));

		String whereClause = SavedGameLevelEntries._ID + "=?";
		String[] whereArgs = new String[] { toUniqueSaveId(level, finished) };

		dataAdapter.insertOrUpdate(SavedGameLevelEntries.TABLE_NAME, null, contentValues, whereClause, whereArgs);
	}

	public boolean isLevelIncompletedAndSaved(int level) {
		return isLevelSaved(level, false);
	}

	public boolean isLevelCompletedAndSaved(int level) {
		return isLevelSaved(level, true);
	}

	private boolean isLevelSaved(int level, boolean isCompleted) {
		String selection = SavedGameLevelEntries.LEVEL + "=?" + " AND "
				+ SavedGameLevelEntries.COMPLETED + "=" + TypesConverter.bool2int(isCompleted);
		String[] selectionArgs = { String.valueOf(level) };
		String[] columns = { SavedGameLevelEntries.LEVEL };
		Cursor cursor = dataAdapter.query(SavedGameLevelEntries.TABLE_NAME,
				columns, selection, selectionArgs, null, null, null);
		return (cursor.getCount() > 0);
	}

	public boolean hasSavedIncompletedLevels() {
		String selection = SavedGameLevelEntries.COMPLETED + "=" + TypesConverter.bool2int(false);
		String[] columns = { SavedGameLevelEntries.LEVEL };
		Cursor cursor = dataAdapter.query(SavedGameLevelEntries.TABLE_NAME,
				columns, selection, null, null, null, null);
		if (cursor == null) {
			return false;
		}
		return (cursor.getCount() > 0);
	}

	public int getLastIncompletedLevelId() {
		String lastSavedTime = "(SELECT MAX(" + SavedGameLevelEntries.SAVED_TIME + ") AS "
				+ SavedGameLevelEntries.SAVED_TIME
				+ " FROM " + SavedGameLevelEntries.TABLE_NAME
				+ " WHERE " + SavedGameLevelEntries.COMPLETED + "= 0)";

		String selection = SavedGameLevelEntries.SAVED_TIME + "=" + lastSavedTime;

		String[] columns = { SavedGameLevelEntries.LEVEL };

		Cursor cursor = dataAdapter.query(SavedGameLevelEntries.TABLE_NAME, columns,
				selection, null, null, null, null);

		if (cursor == null) {
			return 0;
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return 0;
		}

		int level = cursor.getInt(cursor.getColumnIndex(StartGameLevelEntries.LEVEL));
		cursor.close();

		return level;
	}

	private int getLastCompletedLevelId() {
		String sql = "SELECT MAX(" + SavedGameLevelEntries.LEVEL + ") AS " + SavedGameLevelEntries.LEVEL + " FROM "
				+ SavedGameLevelEntries.TABLE_NAME + " WHERE " + SavedGameLevelEntries.COMPLETED + " = 1";
		Cursor cursor = dataAdapter.rawQuery(sql, null);

		if (cursor == null) {
			return 0;
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return 0;
		}

		int lastSavedLevel = cursor.getInt(cursor.getColumnIndex(StartGameLevelEntries.LEVEL));
		cursor.close();
		return lastSavedLevel;
	}

	public boolean isMapLevelAvailable(int level) {

		if (level == 0) {
			return true;
		}

		if (getCompletedLevelsCount() == 0) {
			return false;
		}

		return (level <= (getLastCompletedLevelId() + 1));
	}

	private int getCompletedLevelsCount() {
		String[] columns = { SavedGameLevelEntries.LEVEL };
		String selection = SavedGameLevelEntries.COMPLETED + "=?";
		String[] selectionArgs = { Integer.toString(TypesConverter.bool2int(true)) };
		Cursor cursor = dataAdapter.query(SavedGameLevelEntries.TABLE_NAME, columns, selection, selectionArgs,
				null, null, null);
		if (cursor == null) {
			return 0;
		}
		int completedCount = cursor.getCount();
		cursor.close();
		return completedCount;
	}

	public int getLastLevelId() {
		String[] columns = { StartGameLevelEntries.LEVEL };
		Cursor cursor = dataAdapter.query(StartGameLevelEntries.TABLE_NAME,
				columns, null, null, null, null, null);

		if (cursor == null) {
			return 0;
		}

		if (!cursor.moveToFirst()) {
			cursor.close();
			return 0;
		}

		return (cursor.getCount() - 1);
	}

	private String toUniqueSaveId(int level, boolean finished) {
		return String.valueOf(level) + String.valueOf(finished);
	}
}